#include "httpconn.h"
using namespace std;
//声明HttpConn类里面的资源文件夹
const char* HttpConn::srcDir;
//声明HttpConn里面的用户数量
std::atomic<int> HttpConn::userCount;
//声明HttpConn里面的是否使用ET模式
bool HttpConn::isET;
//HttpConn的构造函数
HttpConn::HttpConn()
{
    //初始化HttpConn的文件描述符为-1
    fd_ = -1;
    //初始化HttpConn的地址为0
    addr_ = {0};
    //初始化HttpConn的是否关闭为true
    isClose_ = true;
}
//HttpConn的析构函数
HttpConn::~HttpConn()
{
    //关闭HttpConn的文件描述符
    Close();
}
//HttpConn类的初始化函数
void HttpConn::init(int fd,const sockaddr_in& addr)
{
    //断言传入的文件描述符大于0
    assert(fd > 0);
    //用户数量加1
    userCount++;
    //将传入的客户端addr结构体赋值给HttpConn的addr_
    addr_ = addr;
    //将传入的文件描述符赋值给HttpConn的fd_
    fd_ = fd;
    //将HttpConn的写缓冲区清空
    writeBuff_.RetrieveAll();
    //将HttpConn的读缓冲区清空
    readBuff_.RetrieveAll();
    //将HttpConn的是否关闭设置为false
    isClose_ = false;
    //打印客户端连接信息
    LOG_INFO("Client[%d](%s:%d) in, userCount:%d",fd_,inet_ntoa(addr_.sin_addr),ntohs(addr_.sin_port),(int)userCount);
}
//关闭HttpConn连接的函数
void HttpConn::Close()
{
    //将HttpConn的响应对象的文件映射区域取消映射
    response_.UnmapFile();
    //如果HttpConn的是否关闭为false
    if(isClose_ == false)
    {
        //将HttpConn是否关闭改为true
        isClose_ = true;
        //用户数量减1
        userCount--;
        //关闭客户端连接的文件描述符
        close(fd_);
        //打印客户端断开连接信息
        LOG_INFO("Client[%d](%s:%d) quit, userCount:%d",fd_,inet_ntoa(addr_.sin_addr),ntohs(addr_.sin_port),userCount.load());
    }
}
//获取HttpConn的文件描述符的函数
int HttpConn::GetFd() const
{
    //返回HttpConn的文件描述符
    return fd_;
}
//获取HttpConn的addr的函数
struct sockaddr_in HttpConn::GetAddr() const
{
    //返回HttpConn的addr
    return addr_;
}
//获取HttpConn的IP的函数
const char* HttpConn::GetIP() const
{
    //返回HttpConn的IP
    return inet_ntoa(addr_.sin_addr);
}
//获取HttpConn的端口的函数
int HttpConn::GetPort() const 
{
    //返回HttpConn的端口
    return addr_.sin_port;
}
//HttpConn类中从缓冲区读取数据的函数
ssize_t HttpConn::read(int* saveErrno)
{
    //声明len变量
    ssize_t len = -1;
    do
    {
        //从文件描述符读取数据到读缓冲区
        len = readBuff_.ReadFd(fd_,saveErrno);
        //如果读取数据失败
        if(len <= 0)
        {
            //跳出循环
            break;
        }
    }while(isET);
    //返回读取到的数据长度
    return len;
}
//HttpConn类中向缓冲区写入数据的函数
ssize_t HttpConn::write(int* saveErrno)
{
    //声明len变量
    ssize_t len = -1;
    do
    {
        //向文件描述符，fd_，从iov_写入数据
        len = writev(fd_,iov_,iovCnt_);
        //如果写入的数据小于0，代表写入失败
        if(len <= 0)
        {
            //设置错误码
            *saveErrno = errno;
            //退出循环
            break;
        }
        //如果写入的数据长度大于0（代表写完了）
        if(iov_[0].iov_len + iov_[1].iov_len == 0)
        {
            //跳出循环
            break;
        }
        //如果处理写入数据量超过第一个缓冲区的情况：
        else if(static_cast<size_t>(len) > iov_[0].iov_len)
        {
            //调整第二个缓冲区的起始位置
            iov_[1].iov_base = (uint8_t*)iov_[1].iov_base + (len - iov_[0].iov_len);
            //调整第二个缓冲区的长度
            iov_[1].iov_len -= (len - iov_[0].iov_len);
            //如果第一个缓冲区的长度还不为零
            if(iov_[0].iov_len)
            {
                //清空第一个缓冲区
                writeBuff_.RetrieveAll();
                //将其长度设置为0
                iov_[0].iov_len = 0;
            }
        }
        //如果处理写入数据量没有超过第一个缓冲区的情况：
        else
        {
            //调整第一个缓冲区的起始位置
            iov_[0].iov_base = (uint8_t*)iov_[0].iov_base + len;
            //调整第一个缓冲区的长度
            iov_[0].iov_len -= len;
            //清空第一个缓冲区
            writeBuff_.Retrieve(len);
        }
     //如果使用边缘触发模式，则需要一次性写完所有数据
     //如果待写入数据长度大于10240，也要继续写入
    }while(isET || ToWriteBytes() > 10240);
}

bool HttpConn::process()
{
    //调用HttpRequest类的Init()方法，初始化HTTP请求对象
    request_.Init();
    //如果读缓冲区可读字节数小于等于0，说明没有收到完整的HTTP请求数据
    if(readBuff_.ReadableBytes() <= 0)
    {
        //返回false
        return false;
    }
    //如果收到的数据>0,说明收到了完整的HTTP请求数据
    else if(request_.parse(readBuff_))
    {
        //进入if，说明解析成功
        //打印请求路径
        LOG_DEBUG("%s",request_.path().c_str());
    }
    //如果解析失败
    else     
    {
        //如果解析失败，则初始化响应对象，并设置为400错误
        response_.Init(srcDir,request_.path(),false,400);
    }
    //走到外面，说明请求已经处理完成
    //生成响应，并添加到writeBuff_缓冲区
    response_.MakeResponse(writeBuff_);
    
    // 创建临时副本存储响应数据，避免指针失效问题
    std::string responseData(writeBuff_.Peek(), writeBuff_.ReadableBytes());
    
    //将响应数据添加到iov_[0]中(只包含响应头)
    iov_[0].iov_base = const_cast<char*>(responseData.data());
    //设置iov_[0]的长度为响应数据的大小
    iov_[0].iov_len = responseData.size();
    //设置有效I/O向量的数量为1
    iovCnt_ = 1;
    //如果响应包含文件内容（文件长度大于0并且文件指针不为空）
    if(response_.FileLen() > 0 && response_.File())
    {
        //指向文件内容的起始地址，通过response_.File()获取
        iov_[1].iov_base = response_.File();
        //设置iov_[1]的长度为文件长度
        iov_[1].iov_len = response_.FileLen();
        //设置有效I/O向量的数量为2(包含响应头和响应体)
        iovCnt_ = 2;
    }
    LOG_DEBUG("filesize:%d, %d  to %d", response_.FileLen() , iovCnt_, ToWriteBytes());
    return true;
}